/**
 * Get the first item that pass the test
 * by second argument function
 *
 * @param {Array} list
 * @param {Function} f
 * @return {*}
 */
export function find(list, f) {
  return list.filter(f)[0]
}

/**
 * Deep copy the given object considering circular structure.
 * This function caches all nested objects and its copies.
 * If it detects circular structure, use cached copy to avoid infinite loop.
 *
 * @param {*} obj
 * @param {Array<Object>} cache
 * @return {*}
 */
export function deepCopy(obj, cache = []) {
  // just return if obj is immutable value
  if (obj === null || typeof obj !== 'object') {
    return obj
  }

  // if obj is hit, it is in circular structure
  const hit = find(cache, c => c.original === obj)
  if (hit) {
    return hit.copy
  }

  const copy = Array.isArray(obj) ? [] : {}
  // put the copy into cache at first
  // because we want to refer it in recursive deepCopy
  cache.push({
    original: obj,
    copy
  })

  Object.keys(obj).forEach(key => {
    copy[key] = deepCopy(obj[key], cache)
  })

  return copy
}

/**
 * forEach for object
 */
export function forEachValue(obj, fn) {
  Object.keys(obj).forEach(key => fn(obj[key], key))
}

export function isObject(obj) {
  return obj !== null && typeof obj === 'object'
}

export function isPromise(val) {
  return val && typeof val.then === 'function'
}

export function assert(condition, msg) {
  if (!condition) throw new Error(`[vuex] ${msg}`)
}

export function partial(fn, arg) {
  return function () {
    return fn(arg)
  }
}
export function getGuid(n = 8) {
  let s4 = () => {
    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
  };
  let Guid = '';
  while (n-- > 0) {
    Guid += s4();
  }
  return Guid;
}
export function getFileExtension(fileName = '') {
  const pattern = /\.([0-9a-z]+)(?:[\?#]|$)/i;
  return (fileName).match(pattern)
}

export function serializeGetParams(params = {}) {
  const paramsArr = []
  const tmpKeyArr = Object.keys(params)
  tmpKeyArr.map((name, index) => {
    paramsArr.push(...[name, '=', params[name], index === tmpKeyArr.length - 1 ? '' : '&'])
  })
  return `?${paramsArr.join('')}`
}
export function isAbsolutePath(url = '') {
  const regExp = new RegExp(/http(s)?:\/\/?/)
  return regExp.test(url)
}
export function deepObjectMerge(config1, config2) {
  for (let key in config2) {
    config1[key] = config1[key] && Object.prototype.toString.call(config1[key]) ===
      "[object Object]" ?
      deepObjectMerge(config1[key], config2[key]) : config2[key];
  }
  return config1;
};
export function _checkIsPromise(obj) {
  if (!obj) {
    return false
  }
  return obj.toString() === '[object Promise]'
}